package pl.edu.agh.dsrg.sr.chat.communicator;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.jgroups.Address;
import org.jgroups.Message;
import org.jgroups.ReceiverAdapter;
import org.jgroups.View;
import org.jgroups.util.Util;

import pl.edu.agh.dsrg.sr.chat.common.ManagementChannelNotificationAppender;
import pl.edu.agh.dsrg.sr.chat.protos.ChatOperationProtos.ChatAction;
import pl.edu.agh.dsrg.sr.chat.protos.ChatOperationProtos.ChatAction.ActionType;
import pl.edu.agh.dsrg.sr.chat.protos.ChatOperationProtos.ChatState;

import com.google.protobuf.InvalidProtocolBufferException;

public class ManagementChannelReceiver extends ReceiverAdapter {

	private ChatCommunicator chatClient;
	private ManagementChannelNotificationAppender notificationAppender;
	private View oldView = null;
	private Object viewLock = new Object();

	public ManagementChannelReceiver(
			ManagementChannelNotificationAppender notificationAppender,
			ChatCommunicator chatClient) {

		super();
		this.notificationAppender = notificationAppender;
		this.chatClient = chatClient;
	}

	@Override
	public void getState(OutputStream output) throws Exception {
		// System.out.println("<<<<< ManagementChannelReceiver.getState()");
		List<ChatAction> actionsList = new ArrayList<ChatAction>();
		synchronized (chatClient.getStateLock()) {
			Map<String, List<String>> chatStateMap = chatClient.getChatState();
			for (String channel : chatStateMap.keySet()) {
				for (String nick : chatStateMap.get(channel)) {
					ChatAction action = ChatAction.newBuilder()
							.setAction(ActionType.JOIN).setChannel(channel)
							.setNickname(nick).build();

					actionsList.add(action);
				}
			}
		}

		ChatState state = ChatState.newBuilder().addAllState(actionsList)
				.build();
//		Util.objectToStream(state, new DataOutputStream(output));
		output.write(state.toByteArray());
	}

	@Override
	public void setState(InputStream input) throws Exception {
		System.out.println("<<<<< ManagementChannelReceiver.setState()");
//		ChatState chatState = (ChatState) Util
//				.objectFromStream(new DataInputStream(input));

		ChatState chatState = ChatState.parseFrom(input);

		
		synchronized (chatClient.getStateLock()) {
			Map<String, List<String>> chatStateMap = chatClient.getChatState();
			for (ChatAction action : chatState.getStateList()) {
				String channel = action.getChannel();
				String nick = action.getNickname();
				List<String> users;
				if (chatStateMap.containsKey(channel)) {
					users = chatStateMap.get(channel);
				} else {
					users = new ArrayList<String>();
				}
				users.add(nick);
				chatStateMap.put(channel, users);
			}
		}

	}

	@Override
	public void viewAccepted(View new_view) {
		// System.out.println("<<<<< ManagementChannelReceiver.viewAccepted()");
		// System.out.println(new_view.getMembers());
		synchronized (viewLock) {

			if (oldView != null) {

				int old_size = oldView.getMembers().size();
				int new_size = new_view.getMembers().size();

				if (new_size < old_size) {
					// remove user from all lists
					Address userToRemove = null;
					for (Address oldUser : oldView.getMembers()) {
						boolean contains = false;

						for (Address newUser : new_view.getMembers()) {
							if (oldUser.equals(newUser)) {
								contains = true;
								break;
							}

						}

						if (!contains) {
							userToRemove = oldUser;
						}
					}

					// System.out.println("TO REM: " + userToRemove);
					String nick = userToRemove.toString();

					synchronized (chatClient.getChatState()) {
						Map<String, List<String>> chatStateMap = chatClient
								.getChatState();
						List<String> users;

						for (String channelName : chatStateMap.keySet()) {
							users = chatStateMap.get(channelName);
							int index = -1;
							for (int i = 0; i < users.size(); i++) {
								if (users.get(i).equalsIgnoreCase(nick)) {
									index = i;
									break;
								}
							}
							if (index != -1) {
								users.remove(index);
								ChatAction action = ChatAction.newBuilder()
										.setAction(ActionType.LEAVE)
										.setChannel(channelName)
										.setNickname(nick).build();
								notificationAppender
										.eventOnManagementChannel(action);

							}
							if (users.size() == 0) {
								chatStateMap.remove(channelName);
							} else {
								chatStateMap.put(channelName, users);
							}

						}
					}

				}
			}
			oldView = new_view;
		}

		// notificationAppender.newView(new_view);
	}

	@Override
	public void receive(Message msg) {
		// System.out.println("<<<<< ManagementChannelReceiver.receive()");
		byte[] raw_stream;
		raw_stream = msg.getBuffer();
		try {
			ChatAction action = ChatAction.parseFrom(raw_stream);
			String channelName = action.getChannel();
			String nick = action.getNickname();
			List<String> users;

			synchronized (chatClient.getChatState()) {
				Map<String, List<String>> chatStateMap = chatClient
						.getChatState();

				if (action.getAction() == ActionType.JOIN) {
					if (chatStateMap.containsKey(channelName)) {
						users = chatStateMap.get(channelName);
					} else {
						users = new ArrayList<String>();
					}
					users.add(nick);
					chatStateMap.put(channelName, users);
				} else if (action.getAction() == ActionType.LEAVE) {
					if (chatStateMap.containsKey(channelName)) {
						users = chatStateMap.get(channelName);
						int index = -1;
						for (int i = 0; i < users.size(); i++) {
							if (users.get(i).equalsIgnoreCase(nick)) {
								index = i;
								break;
							}
						}
						if (index != -1) {
							users.remove(index);
						}
						if (users.size() == 0) {
							chatStateMap.remove(channelName);
						} else {
							chatStateMap.put(channelName, users);
						}

					} else {
						System.err
								.println("ERROR -> channel not present in list, action LEAVE, user: "
										+ nick + ", channel: " + channelName);
					}

				} else {
					System.err.println("UNKNWN ACTION TYPE");
				}
			}

			notificationAppender.eventOnManagementChannel(action);
		} catch (InvalidProtocolBufferException e) {
			e.printStackTrace();
		}
	}
}
